﻿#include <QHostInfo>
#include <QNetworkInterface>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QEventLoop>
#include <QTimer>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonParseError>
#include <QSettings>
#include <QCoreApplication>
#include <QFileIconProvider>
#include <QTemporaryFile>
#include <QDir>
#include <QDesktopServices>
#include <QProcess>
#include <Windows.h>
#include <Shlobj.h>
#include <tlhelp32.h>
#include "qwindowsmanager.h"


/*
    返回主机名
*/
QString QWindowsManager::hostName()
{
    QString hostName;

    DWORD size=0;
    GetComputerName(NULL,&size);
    wchar_t *name=new wchar_t[size];

    GetComputerName(name,&size);
    hostName = QString::fromWCharArray(name);

    delete [] name;
    return hostName;
}

/*
    返回用户名
*/
QString QWindowsManager::userName()
{
    QString usrName;

    DWORD size=0;
    GetUserName(NULL,&size);
    wchar_t *name=new wchar_t[size];

    GetUserName(name,&size);
    usrName = QString::fromWCharArray(name);

    delete [] name;
    return usrName;
}

/*
   返回本机广域网IP。注意如果本地IP是自动分配的，则此函数获取的
   IP可能不准确。
*/
QString QWindowsManager::wanIp()
{
    QEventLoop loop;
    QTimer timer;

    QUrl url =QUrl::fromUserInput("http://whois.pconline.com.cn/ipJson.jsp?json=true");
    QNetworkRequest request(url);
    QNetworkAccessManager netManager;
    QNetworkReply *replay =  netManager.get(request);

    QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
    QObject::connect(replay, &QNetworkReply::finished, &loop, &QEventLoop::quit);

    timer.start(2000);
    loop.exec();

    if(replay->isFinished())
    {
        if(replay->error()==QNetworkReply::NoError)
        {
            QByteArray bytes = replay->readAll();
            QJsonParseError error;
            //Note that the encoding of the return value is ANSI,
            //but the QJSONDocument parses the data in UTF-8 encoding
            QJsonDocument jsonDoc = QJsonDocument::fromJson(QString::fromLocal8Bit(bytes).toUtf8(),&error);
            if(error.error==QJsonParseError::NoError)
            {
                return jsonDoc.object().value("ip").toString();
            }
            else
            {
                qDebug()<<__FUNCTION__<<" : parse replay error,"<<error.errorString();
                return "";
            }
        }
        else
        {
            qDebug()<<__FUNCTION__<<" : request error,"<<replay->errorString();
            return "";
        }
    }
    else
    {
        qDebug()<<__FUNCTION__<<" : request timeout";
        return "";
    }
}

/*
    返回本机局域网IP。
*/
QString QWindowsManager::lanIp()
{
    QString ip;

    QList<QNetworkAddressEntry> addressList = activeNetworkInterface().addressEntries();

    for(int i=0;i<addressList.size();i++)
    {
        if(addressList.at(i).ip().protocol() == QAbstractSocket::IPv4Protocol)
        {
            ip =addressList.at(i).ip().toString();
        }
    }

    return ip;
}

/*
    返回本机MAC地址。
*/
QString QWindowsManager::mac()
{
    return activeNetworkInterface().hardwareAddress();
}

/*
    返回本机当前活动的网卡
*/
QNetworkInterface QWindowsManager::activeNetworkInterface()
{
    QNetworkInterface netInterface;
    QList<QNetworkInterface> nets = QNetworkInterface::allInterfaces();
    int nCnt = nets.count();

    // Also meet the following conditions is the active networkinterface
    // · the networkinterface is running
    // · the networkinterface is not loopback networkinterface.
    // · the hardwareAddress of networkinterface is not contains belows fileds：
    //  "00:05:69"; //vmware1
    //  "00:0C:29"; //vmware2
    //  "00:50:56"; //vmware3
    //  "00:1c:14"; //vmware4
    //  "00:1C:42"; //parallels1
    //  "00:03:FF"; //microsoft virtual pc
    //  "00:0F:4B"; //virtual iron 4
    //  "00:16:3E"; //red hat xen , oracle vm , xen source, novell xen
    //  "08:00:27"; //virtualbox
    for(int i = 0; i < nCnt; i ++)
    {
        if( nets[i].flags().testFlag(QNetworkInterface::IsUp)&&
                nets[i].flags().testFlag(QNetworkInterface::IsRunning)&&
                !nets[i].flags().testFlag(QNetworkInterface::IsLoopBack)&&
                !nets[i].hardwareAddress().contains("00:05:69")&&
                !nets[i].hardwareAddress().contains("00:0C:29")&&
                !nets[i].hardwareAddress().contains("00:50:56")&&
                !nets[i].hardwareAddress().contains("00:1c:14")&&
                !nets[i].hardwareAddress().contains("00:05:69")&&
                !nets[i].hardwareAddress().contains("00:1C:42")&&
                !nets[i].hardwareAddress().contains("00:05:69")&&
                !nets[i].hardwareAddress().contains("00:03:FF")&&
                !nets[i].hardwareAddress().contains("00:0F:4B")&&
                !nets[i].hardwareAddress().contains("00:16:3E")&&
                !nets[i].hardwareAddress().contains("08:00:27"))
        {
            netInterface = nets[i];
            break;
        }
    }

    return netInterface;
}

/*
    将windows错误码转换为对应的字符串消息。
*/
QString QWindowsManager::windowsErrorCodeToMessage(int errorCode)
{
    QString ret;
    wchar_t *string = 0;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL,
                  errorCode,
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                  (LPWSTR)&string,
                  0,
                  NULL);
    ret = QString::fromWCharArray(string);
    LocalFree((HLOCAL)string);

    if (ret.isEmpty() && errorCode == ERROR_MOD_NOT_FOUND)
        ret = QString::fromLatin1("The specified module could not be found.");
    if (ret.endsWith(QLatin1String("\r\n")))
        ret.chop(2);
    if (ret.isEmpty())
        ret = QString::fromLatin1("Unknown error 0x%1.")
                .arg(unsigned(errorCode), 8, 16, QLatin1Char('0'));
    return ret;
}

/*
    将扩展名extension与指定的应用程序progid进行关联。

    progid必须已存在，应用程序的rogid可以在注册表
    HKEY_CURRENT_USER/Software/Classes 查看。

    cover：指定关联已存在时是否覆盖
*/
bool QWindowsManager::createFileAssociation(QString extension, QString progid,bool cover)
{
    QString rootPath ="HKEY_CURRENT_USER\\Software\\Classes";
    QSettings settings(rootPath,QSettings::NativeFormat);

    settings.beginGroup(progid);
    QString currentProgid = settings.value(".","NotExist").toString();
    settings.endGroup();
    if(currentProgid=="NotExist")
    {
        qDebug()<<__FUNCTION__<<" : Progid is not exist";
        return false;
    }

    settings.beginGroup(extension);
    QString currentExt= settings.value(".","NotExist").toString();
    settings.endGroup();
    if(currentExt!="NotExist" && !cover)
    {
        qDebug()<<__FUNCTION__<<" : Extension is already exist";
        return false;
    }

    //Associate the extension group to a progid group
    settings.beginGroup(extension);
    settings.setValue(".",progid);
    settings.endGroup();
    settings.sync();

    //Notifies the system that the file association has changed
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);

    return true;

}

/*
    创建一个新的文件关联。

    extension：需要关联的文件
    progid：应用程序id，必须是全局唯一的。可以使用程序名进行命名，例如QtProject.QtCreator.cpp
    description：描述
    iconPath：关联后的文件图标
    openCmd：运行文件时（例如双击文件）触发的命令行
    cover：指定progid已存在时是否覆盖。
*/
bool QWindowsManager::createFileAssociation(QString extension, QString progid, QString description,QString iconPath,QString openCmd, bool cover)
{
    QString rootPath ="HKEY_CURRENT_USER\\Software\\Classes";
    QSettings settings(rootPath,QSettings::NativeFormat);

    settings.beginGroup(extension);
    QString currentExt = settings.value(".","NotExist").toString();
    settings.endGroup();
    if(currentExt!="NotExist" && !cover)
    {
        qDebug()<<__FUNCTION__<<" : Extension is already exist";
        return false;
    }

    //Create an extension group, and associate the extension group to a progid group
    settings.beginGroup(extension);
    settings.setValue(".",progid);
    settings.endGroup();

    //Create an progid group, and set the description, icon, openCmd
    settings.beginGroup(progid);
    settings.setValue(".",description);
    settings.setValue("DefaultIcon/.",iconPath);
    settings.setValue("shell/Open/Command/.",openCmd);
    settings.endGroup();
    settings.sync();

    //Notifies the system that the file association has changed
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);

    return true;
}

/*
    创建一个桌面右键菜单项

    itemId：菜单项Id，必须是唯一的。
    description：菜单项描述。
    iconPat：菜单项图标。
    openCmd：触发菜单时执行的命令行。
    cover：指定对应Id的菜单已存在时，是否覆盖。
*/
bool QWindowsManager::createContextMenuItem(QString itemId,QString description,QString iconPath, QString openCmd, bool cover)
{
    QString rootPath ="HKEY_CLASSES_ROOT\\Directory\\Background\\shell";
    QSettings settings(rootPath,QSettings::NativeFormat);

    settings.beginGroup(itemId);
    QString currentName = settings.value(".","NotExist").toString();
    settings.endGroup();
    if(currentName!="NotExist" && !cover)
    {
        qDebug()<<__FUNCTION__<<" : Menu item is already exist";
        return false;
    }

    //Create a menuitem group, and set the description, iconPath, openCmd
    settings.beginGroup(itemId);
    settings.setValue(".",description);
    settings.setValue("Icon",iconPath);
    settings.setValue("command/.",openCmd);
    settings.endGroup();
    settings.sync();

    return true;
}

/*
    删除指定id的桌面菜单项
*/
void QWindowsManager::removeContextMenuItem(QString itemId)
{
    QString rootPath ="HKEY_CLASSES_ROOT\\Directory\\Background\\shell";
    QSettings settings(rootPath,QSettings::NativeFormat);

    settings.remove(itemId);
}

/*
    返回指定名称的应用程序是否开启了自动启动。

    appName：该应用程序注册自动启动时使用的名称
    return：返回是否开启了自动启动。
*/
bool QWindowsManager::autoStartEnabled(QString appName)
{
    QString rootPath = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
    QSettings settings(rootPath,QSettings::NativeFormat);

    QString openCmd = settings.value(appName,"NotExist").toString();

    if(openCmd=="NotExist")
    {
        return false;
    }
    else
    {
        return true;
    }
}

/*
    添加自动启动配置。

    appName：程序名称，可以任意，但必须唯一。
    openCmd：启动该程序的命令行。
    bool：开启/关闭自动启动。
*/
void QWindowsManager::setAutoStartEnabled(QString appName,QString openCmd,bool on)
{
    QString rootPath = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
    QSettings settings(rootPath,QSettings::NativeFormat);

    if(on)
    {
        settings.setValue(appName,openCmd);
    }
    else
    {
        settings.remove(appName);
    }
}

/*
    返回环境变量列表。

    name：环境变量名称
    return：环境变量值列表
*/
QStringList QWindowsManager::environmentVariableValue(QString name)
{
    QString rootPath ="HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
    QSettings settings(rootPath,QSettings::NativeFormat);

    QString strValue = settings.value(name).toString();

    return strValue.split(";");
}

/*
    覆盖环境变量。

    name：环境变量名称
    values：环境变量值列表
*/
void QWindowsManager::setEnvironmentVariableValue(QString name, QStringList values)
{
    QString rootPath ="HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
    QSettings settings(rootPath,QSettings::NativeFormat);

    QString strValue;
    for(int i=0;i<values.size();i++)
    {
        strValue.append(values.at(i)+";");
    }

    settings.setValue(name,strValue);
    settings.sync();

    //Send notifications to all programs
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
                         (LPARAM)"Environment", SMTO_ABORTIFHUNG,5000, NULL);
}

/*
    追加环境变量。

    name：环境变量名称
    values：需要追加到该环境变量的值列表
*/
void QWindowsManager::appendEnvironmentVariableValue(QString name, QStringList values)
{
    QString rootPath ="HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
    QSettings settings(rootPath,QSettings::NativeFormat);

    QString oldstrValue = settings.value(name).toString();
    QString strValue;
    for(int i=0;i<values.size();i++)
    {
        strValue.append(values.at(i)+";");
    }

    if(oldstrValue.right(1)!=";" && oldstrValue!=""){
        oldstrValue.append(";");
    }
    oldstrValue.append(strValue);

    settings.setValue(name,oldstrValue);
    settings.sync();

    //Send notifications to all programs
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
                         (LPARAM)"Environment", SMTO_ABORTIFHUNG,5000, NULL);
}

/*
    阻塞式向窗口发送Window消息。

    windowClassName：窗口类名，
    windowTitleName：窗口标题名，与windowClassName可以不同时指定。
    msgId：消息ID。
    data：消息数据，其将存储在消息结构体的LAPARM参数中。
*/
long QWindowsManager::sendMessage(QString windowClassName,QString windowTitleName,unsigned int msgId,void *data)
{
    //Convert QString to wchart_t
    wchar_t *wstrWinClass;
    wchar_t *wstrWinTitle;
    if(windowClassName!="")
    {
        wstrWinClass = new wchar_t[windowClassName.length()];
        windowClassName.toWCharArray(wstrWinClass);
    }else{
        wstrWinClass=NULL;
    }
    if(windowTitleName!="")
    {
        wstrWinTitle = new wchar_t[windowTitleName.length()];
        windowTitleName.toWCharArray(wstrWinTitle);
    }else{
        wstrWinTitle=NULL;
    }

    //Find window handle
    HWND hwnd = ::FindWindow(wstrWinClass,wstrWinTitle);
    if(!hwnd)
    {
        delete [] wstrWinClass;
        delete [] wstrWinTitle;

        qDebug()<<__FUNCTION__<<": failed to find window";
        return -9999;
    }

    delete [] wstrWinClass;
    delete [] wstrWinTitle;

    //Block sending message
    return ::SendMessage(hwnd,msgId,NULL,(LPARAM)data);
}

/*
    非阻塞式向窗口发送Window消息。

    windowClassName：窗口类名，
    windowTitleName：窗口标题名，与windowClassName可以不同时指定。
    msgId：消息ID。
    data：消息数据，其将存储在消息结构体的LAPARM参数中。
*/
bool QWindowsManager::postMessage(QString windowClassName, QString windowTitleName, unsigned int msgId, void *data)
{
    //Convert QString to wchart_t
    wchar_t *wstrWinClass;
    wchar_t *wstrWinTitle;
    if(windowClassName!="")
    {
        wstrWinClass = new wchar_t[windowClassName.length()];
        windowClassName.toWCharArray(wstrWinClass);
    }else{
        wstrWinClass=NULL;
    }
    if(windowTitleName!="")
    {
        wstrWinTitle = new wchar_t[windowTitleName.length()];
        windowTitleName.toWCharArray(wstrWinTitle);
    }else{
        wstrWinTitle=NULL;
    }

    //Find window handle
    HWND hwnd = ::FindWindow(wstrWinClass,wstrWinTitle);
    if(!hwnd)
    {
        delete [] wstrWinClass;
        delete [] wstrWinTitle;

        qDebug()<<__FUNCTION__<<": failed to find window";
        return false;
    }

    delete [] wstrWinClass;
    delete [] wstrWinTitle;

    //Post the message to the message queue
    return ::PostMessage(hwnd,msgId,NULL,(LPARAM)data);
}

/*
    调用本地系统的默认程序打开一个url，该url可以是一个网址、文件
    或一个可执行程序等。

    注意：如果url是一个本地文件，则应使用QUrl::fromLocalFile()
    来创建该url。如果url中包含中文字符，则应使用QStringLiteral()
    来包含它。

    url：需要打开的Url。
    return：返回是否打开成功。
 */
bool QWindowsManager::openUrl(QUrl url)
{

    return QDesktopServices::openUrl(url);
}

/*
   杀死指定PID的进程，并返回是否操作成功。
*/
bool QWindowsManager::killProcess(int processId)
{
    bool ok=true;
    HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,FALSE,processId);
    if(hProcess!=NULL)
    {
        if(!TerminateProcess(hProcess,0))
        {
            ok=false;
        }
    }else{
        ok = false;
    }

    ::CloseHandle(hProcess);
    return ok;
}

/*
    杀死指定名称的进程，并返回是否操作成功
*/
bool QWindowsManager::killProcess(QString processName)
{
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);

    //Gets a snapshot of all current processes on the system
    HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hProcessSnap == INVALID_HANDLE_VALUE)
    {
        qDebug()<<__FUNCTION__<<": failed to get process snapshot";
        return false;
    }

    //Gets all processesId with the same Name as processName
    QVector<unsigned long> pidList;
    BOOL bMore = ::Process32First(hProcessSnap,&pe32);
    while(bMore)
    {
        QString pName = QString::fromWCharArray(pe32.szExeFile);
        unsigned long pId = pe32.th32ProcessID;

        if(pName.contains(processName))
        {
            pidList.push_back(pId);
        }

        bMore = ::Process32Next(hProcessSnap,&pe32);
    }
    ::CloseHandle(hProcessSnap);

    //Kill all the found processes
    if(pidList.size()>0)
    {
        bool ok = true;

        for(int i=0;i<pidList.size();i++)
        {
            HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,FALSE,pidList.at(i));
            if(hProcess!=NULL)
            {
                if(!TerminateProcess(hProcess,0))
                {
                    ok=false;
                }
            }else{
                ok = false;
            }
            ::CloseHandle(hProcess);
        }

        return ok;
    }
    else
    {
        return false;
    }
}

/*
    返回文件的图标。

    filePath：需要获取图标的文件
*/
QIcon QWindowsManager::getFileIcon(QString filePath)
{
    QFileIconProvider iconProvider;
    return iconProvider.icon(QFileInfo(filePath));
}

/*
    返回制定扩展名对应的文件的图标

    fileExtension：文件扩展名
*/
QIcon QWindowsManager::getFileIconWithExtension(QString fileExtension)
{
    QIcon icon;
    QFileIconProvider iconProvider;
    QTemporaryFile tmpFile(QDir::tempPath()+"XXXXXX" + fileExtension);
    tmpFile.setAutoRemove(false);

    if(tmpFile.open())
    {
        QString fileName = tmpFile.fileName();
        tmpFile.write(QByteArray());
        tmpFile.close();
        icon = iconProvider.icon(QFileInfo(fileName));
        tmpFile.remove();
    }
    else
    {
        qDebug()<<__FUNCTION__<<" : failed to create temporary file";
    }

    return icon;
}

